using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Caliburn.Micro;
using MediaOwl.Common;
using MediaOwl.Common.IsolatedStorage;
using MediaOwl.Common.Messages;
using MediaOwl.Common.ScreenManagement;
using MediaOwl.Core.Modularity;
using MediaOwl.Resources;

namespace MediaOwl.ViewModels
{
    [Export]
    public sealed class ModuleViewModel : ExtendedScreen
    {
        #region Fields
        private readonly IEventAggregator eventAggregator;
        private readonly ISettingsManager settingsManager;
        private readonly IModuleManager moduleManager;
        private bool initialized;
        #endregion

        #region Constructor
        [ImportingConstructor]
        public ModuleViewModel(IEventAggregator eventAggregator, ISettingsManager settingsManager, IModuleManager moduleManager)
        {
            this.eventAggregator = eventAggregator;
            this.settingsManager = settingsManager;
            this.moduleManager = moduleManager;
            DisplayName = Captions.ModuleScreenTitle;
        }

        #endregion

        #region Properties with Backing-Fields

        public BindableCollection<Module> Modules
        {
            get { return moduleManager.Modules; }
        }

        private bool saveForNextStart;
        public bool SaveForNextStart
        {
            get { return saveForNextStart; }
            set { saveForNextStart = value; NotifyOfPropertyChange(() => SaveForNextStart);}
        }

        private bool unloadModulesAvailable;
        public bool UnloadModulesAvailable
        {
            get { return unloadModulesAvailable; }
            set { unloadModulesAvailable = value; NotifyOfPropertyChange(() => UnloadModulesAvailable);}
        }

        #endregion

        #region Methods

        protected override void OnActivate()
        {
            base.OnActivate();
            Run.Coroutine(GetLoadedModules());
        }

        public IEnumerable<IResult> LoadModuleList()
        {
            if (initialized)
                yield break;

            yield return Show.Busy(this);

            Settings settings = null;
            yield return settingsManager.GetSettings(x => settings = x, x => eventAggregator.Publish(new ErrorMessage(x)));
            yield return moduleManager.LoadModuleList(x => NotifyOfPropertyChange(() => Modules)
                , x => eventAggregator.Publish(new ErrorMessage(x)));

            if (settings != null && settings.SavedModuleSelection.Count != 0)
                Run.Coroutine(LoadModules(settings.SavedModuleSelection.Select(x => new Uri(x, UriKind.Relative))));

            yield return Show.NotBusy(this);
            initialized = true;
        }

        public IEnumerable<IResult> LoadModules()
        {
            return LoadModules(null);
        }

        private IEnumerable<IResult> LoadModules(IEnumerable<Uri> uriList = null)
        {
            yield return Show.Busy(this);
            var hasError = false;

            if (uriList == null && SaveForNextStart)
            {
                settingsManager.CurrentSettings.SavedModuleSelection.Clear();
                settingsManager.CurrentSettings.SavedModuleSelection.AddRange(Modules.Where(x => x.Selected).Select(x => x.Uri.ToString()));

                yield return settingsManager.SaveSettings();
            }

            var uris = uriList ?? Modules.Where(x => x.Selected).Select(x => x.Uri);

            foreach (var uri in uris)
                yield return moduleManager.LoadModule(uri, () => NotifyOfPropertyChange(() => Modules), x =>
                                                                           {
                                                                               hasError = true;
                                                                               eventAggregator.Publish(new ErrorMessage(x));
                                                                           });
            if (hasError == false)
            {
                yield return Show.Child<MainViewModel>()
                    .In(Parent);
            }

            yield return Show.NotBusy(this);
        }

        public IEnumerable<IResult> RemoveModules()
        {
            return Modules.Where(x => x.Loaded).Select(module =>
                                                       moduleManager.RemoveModule(
                                                           module.Uri,
                                                           () =>
                                                           {
                                                               Modules.Clear();
                                                               Modules.AddRange(moduleManager.Modules);
                                                               Modules.Apply(x => x.Selected = false);
                                                               NotifyOfPropertyChange(() => Modules);
                                                               UnloadModulesAvailable = false;
                                                           },
                                                           x => eventAggregator.Publish(new ErrorMessage(x))));
        }

        public IEnumerable<IResult> GetLoadedModules()
        {
            yield return moduleManager.GetLoadedModules(x => NotifyOfPropertyChange(() => Modules), x => eventAggregator.Publish(new ErrorMessage(x)));
            UnloadModulesAvailable = Modules.Where(x => x.Loaded).Count() > 0;
        }

        #endregion
    }
}